package tesis.service.impl;

import tesis.service.CrudService;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;


@Transactional
@Component("crudService")
public class CrudServiceImpl implements CrudService {

    private static Logger log = LoggerFactory.getLogger(CrudServiceImpl.class);
    @PersistenceContext(unitName ="tesis.domain:default")
    private EntityManager em;
    
    @Override
    public EntityManager getEm() {
        return em;
    }

    public void setEm(EntityManager em) {
        this.em = em;
    }
    
    private Query createNamedQuery(String namedQueryName) {
        return this.em.createNamedQuery(namedQueryName);
    }

    private Query createQuery(String query){
        return this.em.createQuery(query);
    }

    private Query createNativeQuery(String nativeQuery){
        return this.em.createNativeQuery(nativeQuery);
    }

    public <T> T create(T t) {
        log.info("inserting " + t.getClass().getName());
        try {
            this.em.persist(t);
            this.em.flush();
            return t;
        } catch (javax.persistence.EntityExistsException e) {
            return update(t);
        }
    }

    public <T> T refresh(T t){
        this.em.refresh(t);
        return t;
    }

    public <T> T find(Object id, Class<T> type) {
        log.info("finding " + type.getName() + " by id=" + id.toString());
        T obj = this.em.find(type, id);
        this.em.refresh(obj);
        return obj;
    }

    public void delete(Object id, Class type) {
        log.info("deleting " + type.getName());
        delete(this.em.getReference(type, id));
    }

    public void delete(Object t) {
        log.info("deleting " + t.getClass().getName());
        this.em.remove(t);
        this.em.flush();
    }

    public <T> T update(T t) {
        log.info("updating " + t.getClass().getName());
        t = this.em.merge(t);
        this.em.flush();
        this.em.refresh(t);
        return t;
    }

    public List<?> findByNamedQuery(String namedQueryName) {
        return findByNamedQuery(namedQueryName, null, 0, 0);
    }

    public List<?> findByNamedQuery(String namedQueryName, Map<String, Object> parameters) {
        return findByNamedQuery(namedQueryName, parameters, 0, 0);
    }

    public List<?> findByNamedQuery(String namedQueryName, int firstResult, int maxResults) {
        return findByNamedQuery(namedQueryName, null, firstResult, maxResults);
    }

    public List<?> findByNamedQuery(String namedQueryName, Map<String, Object> parameters, int firstResult, int maxResults) {
        log.info("finding by named query :" + namedQueryName);
        Query query = this.em.createNamedQuery(namedQueryName);
        if (firstResult > 0) {
            query.setFirstResult(firstResult);
        }
        if (maxResults > 0) {
            query.setMaxResults(maxResults);
        }
        if (parameters != null) {
            Set<Entry<String, Object>> rawParameters = parameters.entrySet();
            for (Entry<String, Object> entry : rawParameters) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query.getResultList();
    }
    
    public int findCountByNamedQuery(String namedQueryName) {
        log.info("finding count by named query :" + namedQueryName);
        return findCount(this.em.createNamedQuery(namedQueryName), null);
    }

    public int findCountByNamedQuery(String namedQueryName, Map<String, Object> parameters) {
        log.info("finding count by named query :" + namedQueryName);
        return findCount(this.em.createNamedQuery(namedQueryName), parameters);
    }
    
    public int findCountByQuery(String queryString) {
        log.info("finding count by query :" + queryString);
        return findCount(this.em.createQuery(queryString), null);
    }

    public int findCountByQuery(String queryString, Map<String, Object> parameters) {
        log.info("finding count by query :" + queryString);
        return findCount(this.em.createQuery(queryString), parameters);
    }

    protected int findCount(Query query, Map<String, Object> parameters){
        if (parameters != null) {
            Set<Entry<String, Object>> rawParameters = parameters.entrySet();
            for (Entry<String, Object> entry : rawParameters) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        Number count = (Number) query.getSingleResult();
        return count.intValue();
    }
    
    public List<?> findByQuery(String queryString) {
        return findByQuery(queryString, null, 0, 0);
    }
    
    public List<?> findByQuery(String queryString, Map<String, Object> parameters) {
        return findByQuery(queryString, parameters, 0, 0);
    }
    
    public List<?> findByQuery(String queryString, int firstResult, int maxResults) {
        return findByQuery(queryString, null, firstResult, maxResults);
    }

    public List<?> findByQuery(String queryString, Map<String, Object> parameters, int firstResult, int maxResults) {
        log.info("finding by query :" + queryString);
        Query query = this.em.createQuery(queryString);
        if (firstResult > 0) {
            query.setFirstResult(firstResult);
        }
        if (maxResults > 0) {
            query.setMaxResults(maxResults);
        }
        if (parameters != null) {
            Set<Entry<String, Object>> rawParameters = parameters.entrySet();
            for (Entry<String, Object> entry : rawParameters) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query.getResultList();
    }

    public List<?> findByNativeQuery(String nativeQuery, Map<String, Object> parameters) {
        return findByNativeQuery(nativeQuery, parameters, 0, 0);
    }

    public List<?> findByNativeQuery(String nativeQuery, Map<String, Object> parameters, int firstResult, int maxResults) {
        Set<Entry<String, Object>> rawParameters = parameters.entrySet();
        log.info("finding by natived query :" + nativeQuery);
        Query query = this.em.createNativeQuery(nativeQuery);
        if (firstResult > 0) {
            query.setFirstResult(firstResult);
        }
        if (maxResults > 0) {
            query.setMaxResults(maxResults);
        }
        for (Entry<String, Object> entry : rawParameters) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        return query.getResultList();
    }

    public int executeNamedQuery(String namedQueryName) {
        log.info("executing named query :" + namedQueryName);
        return createNamedQuery(namedQueryName).executeUpdate();

 }

    public int executeNamedQuery(String namedQueryName, Map<String, Object> parameters) {
        Set<Entry<String, Object>> rawParameters = parameters.entrySet();
        log.info("executing named query :" + namedQueryName);
        Query query = createNamedQuery(namedQueryName);
        for(Entry<String, Object> entry : rawParameters) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        return query.executeUpdate();
    }

    public int executeQuery(String query) {
        log.info("executing query :" + query);
        return createQuery(query).executeUpdate();
    }

    public int executeQuery(String query, Map<String, Object> parameters) {
        Set<Entry<String, Object>> rawParameters = parameters.entrySet();
        log.info("executing query :" + query);
        Query q = createQuery(query);
        for(Entry<String, Object> entry : rawParameters) {
            q.setParameter(entry.getKey(), entry.getValue());
        }
        return q.executeUpdate();
    }
}
